﻿using System;
using System.ComponentModel;
#if NETFX_CORE
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
#else
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
#endif

namespace FreshMeat.Xaml
{
   /// <summary>
   /// An attached behavior which allows a button to open a window.
   /// The window's type is specified by the <see cref="WindowProperty"/>.
   /// If the data context implements <see cref="IEditableObject"/> and the window is closed using
   /// the <see cref="CloseWindowBehavior"/> then any changes will either be commited or canceled 
   /// depending on the result, all in a very MVVM manner.
   /// then 
   /// </summary>
   /// <remarks>
   /// For example, this button would open "MyChildWindow" (which is derived from XChildWindow) and
   /// sets the window's DataContext to the specified value:
   ///    &lt;Button Content="Open" Xaml:OpenWindowBehavior.Window="MyChildWindow" Xaml:OpenWindowBehavior.DataContext={Binding SomeData}" /&gt;
   /// Whenever the button is clicked, the window will be opened.
   /// </remarks>
   public class OpenWindowBehavior : AttachedBehavior<OpenWindowBehavior, FrameworkElement>
   {
      /// <summary>
      /// The type of window to open.
      /// This type must be derived from <see cref="XChildWindow"/>.
      /// </summary>
      public readonly static DependencyProperty WindowProperty = DependencyProperty.RegisterAttached(
         "Window",
         typeof(Type),
         typeof(OpenWindowBehavior),
         new PropertyMetadata(null, (d, e) => GetInstance(d)));

      public static Type GetWindow(DependencyObject d)
      {
         return (Type)d.GetValue(WindowProperty);
      }

      public static void SetWindow(DependencyObject d, Type value)
      {
         d.SetValue(WindowProperty, value);
      }

      /// <summary>
      /// An alternate data context to be assigned to the window.
      /// If this is null then the button's data context is used instead.
      /// </summary>
      public readonly static DependencyProperty AltDataContextProperty = DependencyProperty.RegisterAttached(
         "AltDataContext",
         typeof(object),
         typeof(OpenWindowBehavior),
         new PropertyMetadata(null, (d, e) => GetInstance(d)));

      public static object GetAltDataContext(DependencyObject d)
      {
         return d.GetValue(AltDataContextProperty);
      }

      public static void SetAltDataContext(DependencyObject d, object value)
      {
         d.SetValue(AltDataContextProperty, value);
      }

      /// <summary>
      /// When attached to something other than a Button, this determine how many clicks it takes to open the window.
      /// Set to 0 to open immediately (useful for items in a combobox), 1 to open on a single click, or 2 to open on a double click.
      /// </summary>
      public readonly static DependencyProperty ClickCountProperty = DependencyProperty.RegisterAttached(
         "ClickCount",
         typeof(int),
         typeof(OpenWindowBehavior),
         new PropertyMetadata(2, (d, e) => GetInstance(d)));

      public static int GetClickCount(DependencyObject d)
      {
         return (int)d.GetValue(ClickCountProperty);
      }

      public static void SetClickCount(DependencyObject d, int value)
      {
         d.SetValue(ClickCountProperty, value);
      }

      protected override void OnAttached()
      {
         var associatedObject = TryGetAssociatedObject();

         if (associatedObject is Button)
            ((Button)associatedObject).Click += OnAssociatedObjectClick;
         else
#if NETFX_CORE
            associatedObject.PointerPressed += OnAssociatedObjectMouseLeftButtonDown;
#else
            associatedObject.MouseLeftButtonDown += OnAssociatedObjectMouseLeftButtonDown;
#endif
      }

      protected override void OnDetaching()
      {
         var associatedObject = TryGetAssociatedObject();
         if (associatedObject == null)
            return;

         if (associatedObject is Button)
            ((Button)associatedObject).Click -= OnAssociatedObjectClick;
         else
#if NETFX_CORE
            associatedObject.PointerPressed -= OnAssociatedObjectMouseLeftButtonDown;
#else
            associatedObject.MouseLeftButtonDown -= OnAssociatedObjectMouseLeftButtonDown;
#endif
      }

      void OnAssociatedObjectClick(object sender, RoutedEventArgs e)
      {
         OpenWindow();
      }

#if NETFX_CORE
      void OnAssociatedObjectMouseLeftButtonDown(object sender, PointerRoutedEventArgs mouseButtonEventArgs)
#else
      void OnAssociatedObjectMouseLeftButtonDown(object sender, MouseButtonEventArgs mouseButtonEventArgs)
#endif
      {
         var associatedObject = TryGetAssociatedObject();
         if (associatedObject == null)
            return;

#if !NETFX_CORE
         var clickCount = GetClickCount(associatedObject);
         if (mouseButtonEventArgs.ClickCount >= clickCount)
#endif
            OpenWindow();
      }

      void OpenWindow()
      {
         var associatedObject = TryGetAssociatedObject();
         if (associatedObject == null)
            return;

         var windowType = GetWindow(associatedObject);

         // The data context from the attached property has precedence, but if it isn't set then
         // we get use the data context from the object to which this behavior is attached.
         var dataContext = GetAltDataContext(associatedObject) ?? associatedObject.DataContext;

#if SILVERLIGHT || NETFX_CORE
         Dialog.ShowModal(windowType, dataContext);
#else
         Dialog.ShowModal(windowType, dataContext, VisualHelper.GetWindow(associatedObject));
#endif
      }

   }
}